Beginners Programming
Number Slide Puzzle: Capturing Sprites Within the Program
Sometimes a graphic design is so simple that it doesn't make sense to clutter the hard drive with numerous bitmaps. In this program, the commands for the tiles remain constant. It is just the number that changes for each tile. Rather than include 24 similar sprites in a zip file, the sprites used for this newsletter's Number Slide Puzzle are created and captured within the program itself. The simple tiles are drawn twice, the first time for the desired color, the second time for the mask. The rules for the mask are
For more detailed information on sprites and masks, check these resources:
Using shades of gray to outline the tiles adds texture to each tile and gives a more professional look; likewise, the multicolored border.
The program starts by first drawing and capturing the required tiles, then drawing the puzzle frame and setting that frame as the background for the sprites. The comments are fairly descriptive as to what each step of coding achieves, so it isn't necessary to reiterate here. Notice that there are no "flush" commands within the program. I'm finding it difficult to determine when to use the "flush" command when sprites are being used. Since the "drawsprites" command causes all "spritevisible spritename on" to be displayed, and a "drawsprites" command is issued with each recognized left mouse click, then it's easy for the user to just left click and redraw the screen that may have been inadvertently erased.
The algorithm for checking to see if each tile is in order consists of four parts.
'Check Part One: (column four, row four)
does tile (4,4) = 0 (i.e., no tile there)?
'Check Part Two: (rows zero through three)
is the tile in column one 1 greater than the tile in column zero?
is the tile in column two 1 greater than the tile in column one?
is the tile in column three 1 greater than the tile in column two?
is the tile in column four 1 greater than the tile in column three?
… since row four (bottom row) only has four tiles (one space must remain empty) then,
'Check Part Three: (row four)
is the tile in column one 1 greater than the tile in column zero?
is the tile in column two 1 greater than the tile in column one?
is the tile in column three 1 greater than the tile in column two?
'Check Part Four: (rows one through four)
is the tile in row one column one 1 greater than the tile in row zero column four?
is the tile in row two column one 1 greater than the tile in row one column four?
is the tile in row three column one 1 greater than the tile in row two column four?
is the rile in row four column one 1 greater than the tile in row three column four?
Any 'no' answer causes the program to return to the 'wait state. If all these conditions are met, then the tiles are in consecutive order and the puzzle is solved. This coding is piecework at best, but it works without crashing and is simple enough to follow. As Tom Nally said, "[it may be best to] dissemble the long lines of code into shorter lines of code, even if it would result in a slower-executing application. In other words . . . deliberately making the code less efficient, with the hope of establishing order and clarity." (Tips For the Hobbyist Programmer, Newsletter #116). Words to live by, Tom! Thanks for the validation.
Two simple graphic commands are then used to reward the puzzle solver. The first is randomly issued sprite orientation commands. Notice that at the end of the loop, each sprite is returned to its normal position. The second is the use of print #handle, "rule rulename" to achieve the flashing effect for "You Did It!" This graphic flashing was inspired by Gordon Rahman (Liberty BASIC Forum: Liberty BASIC Wishlist: Round buttons). Again, for a more detailed explanation of "rule rulename", consult the Liberty Basic Help File and also Liberty BASIC Newsletter #101 (thanks to Alyce again and again).
There was a short burst of conversation regarding sound recently in the Yahoo!Liberty Groups message board. Stefan Pendl (Yahoo! Groups: libertybasic Message #18574) offered some code to achieve sound without the use of .wav files. It seems that different versions of Windows (9x, 2000, NT, ME, XP, etc.) are not all compatible with sound coding, so any ideas of auditory reward were abandoned.
You may want to use the spritemovexy command with a timer delay rather than the spritexy command to "see" the tile "slide" into the empty spot. Be careful, though. If the last tile hasn't completely slid into its new position before the player left clicks for the next move, the x and y coordinates can become less than perfect. If you choose to do this, you may want to flag the spriteMoveXY loop to prevent leftmouseclick action until the loop is completed. Happy puzzling!
'Code follows
'------------------------------------------------------------------------------------------------
''''''''''''''''''''''''''''''''''''
' Slide Puzzle '
' by Janet Terra '
' janetterra@yahoo.com '
''''''''''''''''''''''''''''''''''''
' Made with Liberty BASIC v3.03 '
' by Carl Gundel '
''''''''''''''''''''''''''''''''''''
'slidepuzzle.bas
nomainwin
'Define the Dimensions of the Window
WindowWidth = 304
WindowHeight = 352
'Define the Menu
menu #w, "&Options","&New Puzzle",[newPuzzle], _
"&Same Puzzle",[samePuzzle],|, _
"&Quit",[endProgram]
'Define the Graphic Box
graphicbox #w.g, 5, 5, 286, 286
'Open the Graphic Boxes in the Program's Main Window
open "Slide Puzzle" for window as #w
'Event Controls
#w, "trapclose [endProgram]"
#w.g, "when leftButtonUp [slideTile]"
#w.g, "setfocus"
'Define the Variables
dim tile$(24) '24 slide sprites
dim ord(24) 'prevents repeat numbers
dim dsrd(25) 'shuffled slide positions 1 - 25
empty = 0 'random empty position
dim tile(4,4) 'numbers placed in 5x5 grid
'The Slide Sprites
#w.g, "down"
#w.g, "fill white"
for n = 1 to 24
tile$ = "tile"
if n < 10 then
tile$=tile$;"0"
end if
tile$=tile$;str$(n)
call drawTile n, tile$
tile$(n)=tile$
next n
'Draw the Grid
#w.g, "cls"
#w.g, "fill black"
#w.g, "size 2"
#w.g, "color black"
#w.g, "backcolor brown"
#w.g, "place 1 1"
#w.g, "boxfilled 284 284"
#w.g, "place 6 6"
#w.g, "color darkpink"
#w.g, "backcolor darkpink"
#w.g, "boxfilled 279 279"
#w.g, "place 11 11"
#w.g, "color darkgreen"
#w.g, "backcolor darkgreen"
#w.g, "boxfilled 274 274"
#w.g, "color black"
#w.g, "backcolor lightgray"
#w.g, "place 16 16"
#w.g, "boxfilled 269 269"
'Set the Grid as Background
#w.g, "getbmp grid 0 0 284 284"
#w.g, "background grid"
#w.g, "flush"
#w.g, "discard"
[newPuzzle]
'Shuffle the Tiles
[orderedRandom]
for i = 0 to 24
ord(i) = i
next i
for i = 1 to 24
[ChooseNumber]
rpt = 0
n = int(rnd(1)*24)+1
for j = 1 to i
if ord(n)=0 then
rpt = 1
j = i
end if
next j
if rpt = 1 then [ChooseNumber]
dsrd(i) = n
ord(n)=0
next i
'Choose One Empty Slot
empty = int(rnd(1)*24)+1
dsrd(25)=dsrd(empty)
dsrd(empty)=0
[samePuzzle]
'Assigning Tiles to Grid
ct = 1
for row = 0 to 4
for col = 0 to 4
tile(col,row)=dsrd(ct)
ct = ct+1
next col
next row
'Place the Tiles
y = 17
for row = 0 to 4
x = 17
for col = 0 to 4
if tile(col,row)<>0 then
#w.g, "spritexy ";tile$(tile(col,row));" ";x;" ";y
#w.g, "spritevisible ";tile$(tile(col,row));" on"
end if
x = x + 50
next col
x = 17:y = y + 50
next row
#w.g, "drawsprites"
wait
'Slide Tile
[slideTile]
'Step One: Which Tile to Move
x = MouseX
y = MouseY
if x < 16 or x > 266 then
wait
end if
if y < 16 or y > 266 then
wait
end if
col = int((x-17)/50)
row = int((y-17)/50)
'Step Two: Is It a Valid Tile?
if tile(col,row) = 0 then
msg$ = "Please Choose a Valid Tile"
call message msg$
tm = 500
gosub [timerDelay]
wait
end if
'Step Three: Which Direction to Move
dir = 0
if row > 0 then
if tile(col,row-1) = 0 then
dir = 1
end if
end if
if col < 4 then
if tile(col+1,row) = 0 then
dir = 2
end if
end if
if row < 4 then
if tile(col,row+1) = 0 then
dir = 3
end if
end if
if col > 0 then
if tile(col-1,row) = 0 then
dir = 4
end if
end if
'Step Four: Is that Slot Open?
if dir = 0 then
msg$ = "Please Choose a Valid Tile"
call message msg$
tm = 500
gosub [timerDelay]
wait
end if
'Step Five: Move Tile to New Slot
x = 17+col*50
y = 17+row*50
tile$ = tile$(tile(col,row))
select case dir
case 1
tile(col,row-1)=tile(col,row)
y = y-50
case 2
tile(col+1,row)=tile(col,row)
x = x+50
case 3
tile(col,row+1)=tile(col,row)
y = y+50
case 4
tile(col-1,row)=tile(col,row)
x = x-50
end select
#w.g, "spritexy ";tile$;" ";x;" ";y
tile(col,row)=0
#w.g, "drawsprites"
'Step Six: Are the Tiles In Order?
if tile(4,4) <> 0 then
wait
end if
dsrd = 0
for row = 0 to 3
for col = 0 to 3
if tile(col+1,row) <> tile(col,row)+1 then
dsrd = 1
end if
next col
next row
for col = 0 to 2
if tile(col+1,row) <> tile(col,row)+1 then
dsrd = 1
end if
next col
for row = 0 to 3
if tile(0,row+1) <> tile(4,row)+1 then
dsrd = 1
end if
next row
'If No Tiles Out of Order (dsrd = 0)
if dsrd = 0 then
'Visual Reward for Ordered Tiles
'Tiles Rotate
msg$ = "You Did It!"
for display = 1 to 10
for tile = 1 to 24
show = int(rnd(1)*4)+1
tile$ = tile$(tile)
select case show
case 1
#w.g, "spriteorient ";tile$;" normal"
case 2
#w.g, "spriteorient ";tile$;" mirror"
case 3
#w.g, "spriteorient ";tile$;" flip"
case 4
#w.g, "spriteorient ";tile$;" rotate180"
end select
next tile
#w.g, "drawsprites"
tm = 10
gosub [timerDelay]
#w.g, "drawsprites"
next display
for tile = 1 to 24
tile$ = tile$(tile)
#w.g, "spriteorient ";tile$;" normal"
next tile
#w.g, "drawsprites"
'You Win Flashes
for i = 1 to 3
for j = 1 to 9
select case j
case 1
#w.g, "rule "; _R2_MERGEPENNOT
R2$ = "_R2_XORPEN"
case 2
#w.g, "rule ";_R2_MERGEPEN
R2$ = "_R2_MERGEPEN"
case 3
#w.g, "rule "; _R2_NOTCOPYPEN
R2$ = "_R2_NOTCOPYPEN"
case 4
#w.g, "rule "; _R2_NOT
R2$ = "_R2_NOT"
case 5
#w.g, "rule "; _R2_MASKPENNOT
R2$ = "_R2_MASKPENNOT"
case 6
#w.g, "rule "; _R2_NOTMERGEPEN
R2$ = "_R2_NOTMERGEPEN"
case 7
#w.g, "rule "; _R2_NOTMASKPEN
R2$ = "_R2_NOTMASKPEN"
case 8
#w.g, "rule "; _R2_XORPEN
R2$ = "_R2_XORPEN"
case 9
#w.g, "rule "; _R2_NOTXORPEN
R2$ = "_R2_NOTXORPEN"
end select
call message msg$
#w.g, "color darkpink"
#w.g, "backcolor green"
#w.g, "size 5"
#w.g, "place 105 132"
#w.g, "boxfilled 186 157"
tm = 50
gosub [timerDelay]
next j
next i
#w.g, "rule "; _R2_COPYPEN
#w.g, "drawsprites"
call message msg$
#w.g, "color darkpink"
#w.g, "size 5"
#w.g, "place 105 132"
#w.g, "box 186 157"
end if
'Step Seven: Await Next Command
wait
sub message msg$
#w.g, "color yellow"
#w.g, "backcolor brown"
#w.g, "font times_new_roman 12 bold"
#w.g, "stringwidth? msg$ width"
#w.g, "place ";144-width/2;" 150"
#w.g, "\";msg$
end sub
[timerDelay]
timer tm, [delay]
wait
[delay]
timer 0
#w.g, "drawsprites"
return
sub drawTile n, tile$
n$ = str$(n)
#w.g, "size 5"
#w.g, "color black"
#w.g, "backcolor black"
#w.g, "place 1 1"
#w.g, "boxfilled 50 50"
#w.g, "color darkgray"
#w.g, "backcolor darkblue"
#w.g, "size 5"
#w.g, "place 1 51"
#w.g, "boxfilled 50 100"
#w.g, "color darkblue"
#w.g, "size 1"
#w.g, "box 50 99"
#w.g, "color darkgray"
#w.g, "font times_new_roman 24 bold"
#w.g, "stringwidth? n$ width"
#w.g, "place ";25-width/2;" ";85
#w.g, "\";n
#w.g, "getbmp ";tile$;" 1 1 50 100"
#w.g, "addsprite ";tile$;" ";tile$
#w.g, "spritevisible ";tile$;" off"
end sub
[endProgram]
confirm "Quit this program? ";ans$
if ans$ <> "yes" then
wait
end if
close #w
for i = 1 to 24
unloadbmp tile$(i)
next i
unloadbmp "grid"
end
'Code ends
'------------------------------------------------------------------------------------------------